import * as _ from 'lodash';
import Graceful from 'node-graceful';

import { logger } from '$common';
import { AgentContext } from './agentContext';
import { ConnectingState } from './state/connectingState';
import { SimulationJobController } from '$domain/model/simulation';
import { version } from '../../../version';

function uncaughtExceptionHandler(err) {
  if (!(err instanceof Error)) {
    err = new Error(String(err));
  }
  if (err.name === 'Error') {
    err.name = 'unhandledExceptionError';
  }
  logger.error(err);
}

function unhandledRejectionHandler(err) {
  if (!(err instanceof Error)) {
    const newError = new Error(String(err));
    // err maybe an object, try to copy the name, message and stack to the new error instance
    if (err) {
      if (err.name) { newError.name = err.name; }
      if (err.message) { newError.message = err.message; }
      if (err.stack) { newError.stack = err.stack; }
    }
    err = newError;
  }

  if (err.name === 'Error') {
    err.name = 'unhandledRejectionError';
  }
  logger.error(err);
}

process.on('uncaughtException', uncaughtExceptionHandler);
process.on('unhandledRejection', unhandledRejectionHandler);

export class Agent {

  constructor() { this.init(); }

  private init() {
    Graceful.on('exit', this.onExit);
  }

  private onExit = async (signal: string) => {
    logger.info('[Agent:bootstrap] receive signal %s, closing', signal);
    await SimulationJobController.stopProcessingJobs();
    logger.info('[Agent:bootstrap] close done.');
  }

  async setup() {
    logger.info('Agent %s start ...', version);
    const ctx = new AgentContext();
    ctx.transitionTo(new ConnectingState());
    await ctx.establishConnection();
  }
}